package httpserver.response;

import httpserver.HTTPException;
import httpserver.general.HTTPHeaders;
import httpserver.general.HTTPMethods;
import httpserver.general.HTTPStatusCodes;
import httpserver.general.Header;
import httpserver.general.HeaderList;
import httpserver.general.MIMETypes;
import httpserver.general.cookie.Cookie;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Date;
import sun.misc.BASE64Decoder;

public class Response extends HTTPHeaders {

    private final String SERVER_NAME = "http-server-zstu";
    private final String STORAGE_DIR_NAME = "www";
    private final String STORAGE_ROOT = File.separator + STORAGE_DIR_NAME;
    private final String separator = File.separator;
    private final char separatorChar = File.separatorChar;
    private final String DEFAULT_FILE = "index.html";
    private final String DEFAULT_MIME = MIMETypes.MIME_TXT;
    private final String AUTH_DIR = "auth";
    private final String AUTH_FILE = AUTH_DIR + separator + DEFAULT_FILE;
    
    private String authParams;
    private boolean authCommitted = false;

    private String uri;
    private String method;
    private OutputStream out;

    public Response(OutputStream out, String method, String uri) {
        this.uri = uri;
        this.out = out;
        this.method = method;
    }

    private String getResponseLine(int code){
        //TODO: change status phrase
        return "HTTP/1.1 " + String.valueOf(code) + " 8D";
    }

    public void setAuthParams(String authParams){
        this.authParams = authParams;
    }
    
    public ByteArrayOutputStream getRequestedResource()
            throws HTTPException, IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        File requestedFile = new File('.'+ STORAGE_ROOT + separator + uri);
        if(getFileExtention().equals("") || requestedFile.isDirectory()){
            uri+= separator + DEFAULT_FILE;
        }
        try{
            FileInputStream fis = new FileInputStream(
                    '.' + STORAGE_ROOT + separator + uri);
            int b;
            while((b=fis.read())!=-1){
                baos.write(b);
            }
            fis.close();
        } catch(FileNotFoundException e) {
            throw new HTTPException(HTTPStatusCodes.HTTP_NOT_FOUND,
                    "page not found");
        } catch(IOException e){
            throw e;
        }
        return baos;
    }
    
    private String getErrorPhrase(int code){
        return "Error " + String.valueOf(code) + "!" +
               "\nPlease check the correctness of URL.";
    }

    private String getFileExtention(){
        for(int i=uri.length()-1; i>-1; i--){
            if(uri.charAt(i)==separatorChar){
                return "";
            } else {
                if(uri.charAt(i)=='.'){
                    return uri.substring(i+1);
                }
            }
        }
        return "";
    }
    
    private boolean isAuthRequired() throws IOException, HTTPException {
        boolean authRequired = false;    
        String a = uri;
        a = a.replace('/', separatorChar);
        a = a.replace('\\', separatorChar);
        a = a.replace(separator+separator, separator);
        if(uri.contains(AUTH_DIR)){
            authRequired = true;
            if(!authParams.isEmpty()){
                BASE64Decoder decoder = new BASE64Decoder();
                byte[] b = decoder.decodeBuffer(authParams);
                String ap = new String(b);
                String ap2[] = ap.split(":");
                if(ap2.length==2&&(ap2[0].equals("login")&&ap2[1].equals("pass"))){
                    authRequired = false;
                    authCommitted = true;
                }
            }
        }
        return authRequired;
    }
    
    private ByteArrayOutputStream getResponseString(int code, String message)
            throws IOException {
        // requested resource
        String resourceLength = "";
        ByteArrayOutputStream resource = null;
        String resourceType = "";
         
        boolean authRequired = false;
        if(method.equals(HTTPMethods.GET)){
            try{
                if(isAuthRequired()){
                    authRequired=true;
                    throw new HTTPException(HTTPStatusCodes.HTTP_UNAUTHORIZED);
                }
                resource = getRequestedResource();
                resourceType = MIMETypes.getType(getFileExtention());
                if(resourceType==null){
                   resourceType = DEFAULT_MIME;
                }
            } catch(HTTPException e){
                code = e.getStatusCode();
                resource = new ByteArrayOutputStream();
                resource.write(getErrorPhrase(code).getBytes());
                resourceType = "text/plain; charset=windows-1251";
            }
        } else if(method.equals(HTTPMethods.POST)){
            // echo mode for POST requests
            resource = new ByteArrayOutputStream();
            resource.write(message.getBytes());
            resourceType = MIMETypes.MIME_HTML;
        } 

                
        // headers
        HeaderList headerList = new HeaderList();
        headerList.addHeader(new Header(HEADER_DATE, new Date().toGMTString()));
        headerList.addHeader(new Header(HEADER_SERVER, SERVER_NAME));
        resourceLength = String.valueOf(resource.size());
        headerList.addHeader(new Header(HEADER_CONTENT_LENGTH, resourceLength));
        headerList.addHeader(new Header(HEADER_CONTENT_TYPE, resourceType));
        if(authCommitted){
            Cookie auth = new Cookie("auth", "committed",
                    "Thu, 15-Nov-2012 22:40:46 GMT",
                    null, "/auth", java.net.InetAddress.getLocalHost().toString(), false);
            headerList.addHeader(new Header(HEADER_SET_COOKIE, auth.toHeaderString()));
        }
        if(authRequired){
            headerList.addHeader(new Header(HEADER_WWW_AUTHENTICATE,
                    "Basic realm=\"Secret zone\""));
        }
        headerList.addHeader(new Header(HEADER_CONNECTION, "close"));

        // complete string
        ByteArrayOutputStream request = new ByteArrayOutputStream();
        request.write(getResponseLine(code).getBytes());
        request.write("\r\n".getBytes());
        request.write(headerList.toString().getBytes());
        request.write("\r\n".getBytes());
        if(!method.equals(HTTPMethods.HEAD)){
            request.write(resource.toByteArray());
        }

        return request;
    }

    public void write(int code) throws IOException {
        ByteArrayOutputStream requestString = getResponseString(code, null);
        out.write(requestString.toByteArray());
        out.flush();
    }

    public void writeCustomMessage(String message) throws IOException {
        ByteArrayOutputStream requestString = getResponseString(
                HTTPStatusCodes.HTTP_OK, message);
        out.write(requestString.toByteArray());
        out.flush();
    }
}
